use std::{str::FromStr, time::Duration};

use web_sys::{Event, KeyboardEvent, MouseEvent, WheelEvent};

use super::{WinRef, Window};

pub struct WinMouse<'a>(pub &'a MouseEvent);
impl<'a> types::IntoEvt<Window, types::Mouse> for WinMouse<'a> {
	fn to_evt(&self, _win: WinRef<'_>) -> Option<types::Mouse> {
		let button = match self.0.button() {
			0 => types::Button::Left,
			1 => types::Button::Middle,
			2 => types::Button::Right,
			3 => types::Button::Mouse(0),
			4 => types::Button::Mouse(1),
			_ => return None,
		};
		let phase = match self.0.type_().as_str() {
			"mousedown" => types::Phase::Start,
			"mousemove" => types::Phase::Update,
			"mouseup" => types::Phase::End,
			_ => return None,
		};
		let point = (self.0.x() as f32, self.0.y() as f32);
		let metas = {
			let mut metas = types::Metas::EMPTY;
			if self.0.shift_key() {
				metas |= types::Metas::SHIFT;
			}
			if self.0.ctrl_key() {
				metas |= types::Metas::CONTROL;
			}
			if self.0.alt_key() {
				metas |= types::Metas::ALT;
			}
			if self.0.meta_key() {
				metas |= types::Metas::META;
			}
			metas
		};
		let nanos = self.0.time_stamp() * 1_000_000.;
		let time = Duration::from_nanos(nanos as _);
		Some(types::Mouse::new(button, phase, point, metas, time))
	}
}
pub struct WinWheel<'a>(pub &'a WheelEvent);
impl<'a> types::IntoEvt<Window, types::Wheel> for WinWheel<'a> {
	fn to_evt(&self, _win: WinRef<'_>) -> Option<types::Wheel> {
		let x = self.0.delta_x() as f32;
		let y = self.0.delta_y() as f32;
		let z = self.0.delta_z() as f32;
		match self.0.delta_mode() {
			WheelEvent::DOM_DELTA_PIXEL => Some(types::Wheel::Pixel(x, y, z)),
			WheelEvent::DOM_DELTA_LINE => Some(types::Wheel::Line(x, y, z)),
			WheelEvent::DOM_DELTA_PAGE => Some(types::Wheel::Page(x, y, z)),
			_ => None,
		}
	}
}
pub struct WinKeyboard<'a>(pub &'a KeyboardEvent);
impl<'a> types::IntoEvt<Window, types::Keyboard> for WinKeyboard<'a> {
	fn to_evt(&self, _win: WinRef<'_>) -> Option<types::Keyboard> {
		let key = types::Key::from_str(&self.0.key()).unwrap_or(types::Key::Unidentified(0));
		let code = types::Code::from_str(&self.0.code()).unwrap_or(types::Code::Unidentified(0));
		let state = match self.0.type_().as_str() {
			"keydown" => types::State::Down,
			"keyup" => types::State::Up,
			_ => return None,
		};
		let location = match self.0.location() {
			KeyboardEvent::DOM_KEY_LOCATION_STANDARD => types::Location::Standard,
			KeyboardEvent::DOM_KEY_LOCATION_NUMPAD => types::Location::Numpad,
			KeyboardEvent::DOM_KEY_LOCATION_LEFT => types::Location::Left,
			KeyboardEvent::DOM_KEY_LOCATION_RIGHT => types::Location::Right,
			_ => return None,
		};
		let metas = {
			let mut metas = types::Metas::EMPTY;
			if self.0.shift_key() {
				metas |= types::Metas::SHIFT;
			}
			if self.0.ctrl_key() {
				metas |= types::Metas::CONTROL;
			}
			if self.0.alt_key() {
				metas |= types::Metas::ALT;
			}
			if self.0.meta_key() {
				metas |= types::Metas::META;
			}
			metas
		};
		let repeat = self.0.repeat();
		let composing = self.0.is_composing();
		Some(types::Keyboard::new(key, code, state, location, metas, repeat, composing))
	}
}
pub struct WinTouch<'a>(pub &'a Event);
impl<'a> types::IntoEvt<Window, types::Touch> for WinTouch<'a> {
	fn to_evt(&self, _win: WinRef<'_>) -> Option<types::Touch> {
		None
	}
}
